#include <stdio.h>
#include <gtest/gtest.h>
#include <properties.h>

#define PERSISTENT_PROPERTY_DIR "/dev/properties.watch/"
#define NOPERSISTENT_PROPERTY_DIR PERSISTENT_PROPERTY_DIR

#define PERSIST_KEY_WITHOUT_PRESET    "persist.test.withoutset"
#define NONPERSIST_KEY_WITHOUT_PRESET "nopersist.test.withoutset"

#define PERSIST_KEY_WITH_PRESET    "persist.test.withset"
#define NONPERSIST_KEY_WITH_PRESET "nopersist.test.withset"

#define PERSIST_SAMEKEY_DIFF_CALLBACK "persist.samekey.diffcb"
#define PERSIST_SAMEKEY_SAME_CALLBACK "persist.samekey.samecb"

#define PERSIST_RM_SPE_CALLBACK "persist.remove.speccb"
#define PERSIST_RM_ALL_CALLBACKS "persist.remove.allcb"

#define PERSIST_RM_ALL_CALLBACKS_LEFT "persist.remove.left"

char persistWithoutPreSetValue[PROP_VALUE_MAX]       = {0};
char nonPersistWithoutPreSetValue[PROP_VALUE_MAX]    = {0};

char persistWithPreSetValue[PROP_VALUE_MAX]       = {0};
char nonPersistWithPreSetValue[PROP_VALUE_MAX]    = {0};

char persistSameKeyDiffCallbackValue1[PROP_VALUE_MAX] = {0};
char persistSameKeyDiffCallbackValue2[PROP_VALUE_MAX] = {0};

char persistSameKeySameCallbackValue[PROP_VALUE_MAX] = {0};

char persistRmSpecCallbackValue1[PROP_VALUE_MAX] = {0};
char persistRmSpecCallbackValue2[PROP_VALUE_MAX] = {0};

char persistRmAllCallbacksValues[3][PROP_VALUE_MAX] = {0};
char persistRmAllCallbacksValuesLeft[PROP_VALUE_MAX] = {0};

void watchPersistWithOutPreSetCallback(void* data) {
    int result = property_get(PERSIST_KEY_WITHOUT_PRESET, persistWithoutPreSetValue, "fail");
    EXPECT_EQ(result > 0, 1);
}

void watchNonPersistWithOutPreSetCallback(void* data) {
    int result = property_get(NONPERSIST_KEY_WITHOUT_PRESET, nonPersistWithoutPreSetValue, "fail");
    EXPECT_EQ(result > 0, 1);
}

void watchPersistWithPreSetCallback(void* data) {
    int result = property_get(PERSIST_KEY_WITH_PRESET, persistWithPreSetValue, "fail");
    EXPECT_EQ(result > 0, 1);
}

void watchNonPersistWithPreSetCallback(void* data) {
    int result = property_get(NONPERSIST_KEY_WITH_PRESET, nonPersistWithPreSetValue, "fail");
    EXPECT_EQ(result > 0, 1);
}

void watchPersistSameKeyDiffCallback1(void* data) {
    int result = property_get(PERSIST_SAMEKEY_DIFF_CALLBACK, persistSameKeyDiffCallbackValue1, "fail");
    EXPECT_EQ(result > 0, 1);
}

void watchPersistSameKeyDiffCallback2(void* data) {
    int result = property_get(PERSIST_SAMEKEY_DIFF_CALLBACK, persistSameKeyDiffCallbackValue2, "fail");
    EXPECT_EQ(result > 0, 1);
}

void watchPersistSameKeySameCallback(void* data) {
    int result = property_get(PERSIST_SAMEKEY_SAME_CALLBACK, persistSameKeySameCallbackValue, "fail");
    EXPECT_EQ(result > 0, 1);
}

void watchPersistRmSpecCallback1(void* data) {
    int result = property_get(PERSIST_RM_SPE_CALLBACK, persistRmSpecCallbackValue1, "fail");
    EXPECT_EQ(result > 0, 1);
}

void watchPersistRmSpecCallback2(void* data) {
    int result = property_get(PERSIST_RM_SPE_CALLBACK, persistRmSpecCallbackValue2, "fail");
    EXPECT_EQ(result > 0, 1);
}

void watchPersistRmAllCallback1(void* data) {
    int result = property_get(PERSIST_RM_ALL_CALLBACKS, persistRmAllCallbacksValues[0], "fail");
    EXPECT_EQ(result > 0, 1);
}

void watchPersistRmAllCallback2(void* data) {
    int result = property_get(PERSIST_RM_ALL_CALLBACKS, persistRmAllCallbacksValues[1], "fail");
    EXPECT_EQ(result > 0, 1);
}

void watchPersistRmAllCallback3(void* data) {
    int result = property_get(PERSIST_RM_ALL_CALLBACKS, persistRmAllCallbacksValues[2], "fail");
    EXPECT_EQ(result > 0, 1);
}

void watchPersistRmAllCallbackLeft(void* data) {
    int result = property_get(PERSIST_RM_ALL_CALLBACKS_LEFT, persistRmAllCallbacksValuesLeft, "fail");
    EXPECT_EQ(result > 0, 1);
}

TEST(PropertyTest, WatchPersistKeyWithoutPreSet) {
    int result = property_set(PERSIST_KEY_WITHOUT_PRESET, "");
    EXPECT_EQ(result, 0);

    char tempPath[1024];
    snprintf(tempPath, sizeof(tempPath), "%s/%s", PERSISTENT_PROPERTY_DIR, PERSIST_KEY_WITHOUT_PRESET);
    remove(tempPath);

    result = property_add_watch(PERSIST_KEY_WITHOUT_PRESET, watchPersistWithOutPreSetCallback, NULL);
    EXPECT_EQ(result, 0);
    sleep(1);

    result = property_set(PERSIST_KEY_WITHOUT_PRESET, "set");
    EXPECT_EQ(result, 0);
    sleep(1);

    EXPECT_STREQ("set", persistWithoutPreSetValue);
}

TEST(PropertyTest, WatchNonPersistKeyWithoutPreSet) {
    int result = property_set(NONPERSIST_KEY_WITHOUT_PRESET, "");
    EXPECT_EQ(result, 0);

    char tempPath[1024];
    snprintf(tempPath, sizeof(tempPath), "%s/%s", NOPERSISTENT_PROPERTY_DIR, NONPERSIST_KEY_WITHOUT_PRESET);
    remove(tempPath);

    result = property_add_watch(NONPERSIST_KEY_WITHOUT_PRESET, watchNonPersistWithOutPreSetCallback, NULL);
    EXPECT_EQ(result, 0);
    sleep(1);

    result = property_set(NONPERSIST_KEY_WITHOUT_PRESET, "set");
    EXPECT_EQ(result, 0);
    sleep(1);

    EXPECT_STREQ(nonPersistWithoutPreSetValue, "set");
}

TEST(PropertyTest, WatchPersistKeyWithPreset) {
    int result = property_set(PERSIST_KEY_WITH_PRESET, "init");
    EXPECT_EQ(result, 0);

    result = property_add_watch(PERSIST_KEY_WITH_PRESET, watchPersistWithPreSetCallback, NULL);
    EXPECT_EQ(result, 0);
    sleep(1);

    result = property_set(PERSIST_KEY_WITH_PRESET, "set");
    EXPECT_EQ(result, 0);
    sleep(1);

    EXPECT_STREQ(persistWithPreSetValue, "set");
}

TEST(PropertyTest, WatchNonPersistKeyWithPreset) {
    int result = property_set(NONPERSIST_KEY_WITH_PRESET, "init");
    EXPECT_EQ(result, 0);

    result = property_add_watch(NONPERSIST_KEY_WITH_PRESET, watchNonPersistWithPreSetCallback, NULL);
    EXPECT_EQ(result, 0);
    sleep(1);

    result = property_set(NONPERSIST_KEY_WITH_PRESET, "set");
    EXPECT_EQ(result, 0);
    sleep(1);

    EXPECT_STREQ(nonPersistWithPreSetValue, "set");
}

TEST(PropertyTest, WatchSameKeySameCallback) {
    int result = property_add_watch(PERSIST_SAMEKEY_SAME_CALLBACK, watchPersistSameKeySameCallback, NULL);
    EXPECT_EQ(result, 0);

    result = property_add_watch(PERSIST_SAMEKEY_SAME_CALLBACK, watchPersistSameKeySameCallback, NULL);
    EXPECT_EQ(result, -1);
}

TEST(PropertyTest, WatchSameKeyDiffCallbackOK) {

    int result = property_set(PERSIST_SAMEKEY_DIFF_CALLBACK, "init");
    EXPECT_EQ(result, 0);

    result = property_add_watch(PERSIST_SAMEKEY_DIFF_CALLBACK, watchPersistSameKeyDiffCallback1, NULL);
    EXPECT_EQ(result, 0);

    result = property_add_watch(PERSIST_SAMEKEY_DIFF_CALLBACK, watchPersistSameKeyDiffCallback2, NULL);
    EXPECT_EQ(result, 0);
    sleep(1);

    for (int i = 0; i < 2; i++) {
        char valStr[8] = {0};
        sprintf(valStr, "%d", i);
        result = property_set(PERSIST_SAMEKEY_DIFF_CALLBACK, valStr);
        EXPECT_EQ(result, 0);
        sleep(1);

        EXPECT_STREQ(persistSameKeyDiffCallbackValue1, valStr);
        EXPECT_STREQ(persistSameKeyDiffCallbackValue2, valStr);
    }
}

TEST(PropertyTest, RemoveSpecificWatchCallback) {

    int result = property_set(PERSIST_RM_SPE_CALLBACK, "clear");
    EXPECT_EQ(result, 0);
    sleep(1);

    result = property_add_watch(PERSIST_RM_SPE_CALLBACK, watchPersistRmSpecCallback1, NULL);
    EXPECT_EQ(result, 0);

    result = property_add_watch(PERSIST_RM_SPE_CALLBACK, watchPersistRmSpecCallback2, NULL);
    EXPECT_EQ(result, 0);

    result = property_set(PERSIST_RM_SPE_CALLBACK, "init");
    EXPECT_EQ(result, 0);
    sleep(1);

    EXPECT_STREQ(persistRmSpecCallbackValue1, "init");
    EXPECT_STREQ(persistRmSpecCallbackValue2, "init");

    result = property_rm_watch_by_callback(PERSIST_RM_SPE_CALLBACK, watchPersistRmSpecCallback1);
    EXPECT_EQ(result, 0);

    result = property_set(PERSIST_RM_SPE_CALLBACK, "remove");
    EXPECT_EQ(result, 0);
    sleep(2);

    EXPECT_STREQ(persistRmSpecCallbackValue1, "init");
    EXPECT_STREQ(persistRmSpecCallbackValue2, "remove");
}

TEST(PropertyTest, RemoveAllWatchByKey) {
    int result = property_set(PERSIST_RM_ALL_CALLBACKS_LEFT, "clear");
    EXPECT_EQ(result, 0);
    sleep(1);

    result = property_add_watch(PERSIST_RM_ALL_CALLBACKS_LEFT, watchPersistRmAllCallbackLeft, NULL);
    EXPECT_EQ(result, 0);

    result = property_set(PERSIST_RM_ALL_CALLBACKS, "clear");
    EXPECT_EQ(result, 0);
    sleep(1);

    result = property_add_watch(PERSIST_RM_ALL_CALLBACKS, watchPersistRmAllCallback1, NULL);
    EXPECT_EQ(result, 0);

    result = property_add_watch(PERSIST_RM_ALL_CALLBACKS, watchPersistRmAllCallback2, NULL);
    EXPECT_EQ(result, 0);

    result = property_add_watch(PERSIST_RM_ALL_CALLBACKS, watchPersistRmAllCallback3, NULL);
    EXPECT_EQ(result, 0);

    result = property_set(PERSIST_RM_ALL_CALLBACKS, "init");
    EXPECT_EQ(result, 0);
    sleep(1);

    EXPECT_STREQ(persistRmAllCallbacksValues[0], "init");
    EXPECT_STREQ(persistRmAllCallbacksValues[1], "init");
    EXPECT_STREQ(persistRmAllCallbacksValues[2], "init");

    result = property_rm_watch(PERSIST_RM_ALL_CALLBACKS);
    EXPECT_EQ(result, 0);

    result = property_set(PERSIST_RM_ALL_CALLBACKS, "remove");
    EXPECT_EQ(result, 0);
    sleep(1);

    EXPECT_STREQ(persistRmAllCallbacksValues[0], "init");
    EXPECT_STREQ(persistRmAllCallbacksValues[1], "init");
    EXPECT_STREQ(persistRmAllCallbacksValues[2], "init");

    result = property_set(PERSIST_RM_ALL_CALLBACKS_LEFT, "left");
    EXPECT_EQ(result, 0);
    sleep(1);

    EXPECT_STREQ(persistRmAllCallbacksValuesLeft, "left");
}

